package com.yizhang.newfeatures;

import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Author: yi.zhang
 * @CreateTime: 2025-08-27 15:50:16
 * @Description: 统计练习
 */
public class Statistics {

    private static final String fileUrl = "E:\\zhang\\project\\personal\\springboot_study\\springboot_01\\data.txt";

    /*
    数据格式
    0       1       2       3       4       5       6     7      8        9     10    11
    序号    下单时间  订单编号  商品编号 类别编号  类别码   品牌   价格   用户编号   年龄   性别   地区
 */
    static final int INDEX = 0;
    static final int TIME = 1;
    static final int ORDER_ID = 2;
    static final int PRODUCT_ID = 3;
    static final int CATEGORY_ID = 4;
    static final int CATEGORY_CODE = 5;
    static final int BRAND = 6;
    static final int PRICE = 7;
    static final int USER_ID = 8;
    static final int USER_AGE = 9;
    static final int USER_SEX = 10;
    static final int USER_REGION = 11;
    static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");

    public static void main(String[] args) {
        case11();
    }

    /**
     * 每月的销售量 1970-01 订单数1307
     */
    public static void case1() {
        try (Stream<String> lines = Files.lines(Path.of(fileUrl))) {
            lines.skip(1).map(e -> e.split(",")[TIME])
                    .collect(Collectors.groupingBy(s ->
                                    YearMonth.from(formatter.parse(s)),
                            () -> new TreeMap<>((o1, o2) -> o2.compareTo(o1)),  // TreeMap可以用来排序 倒序
                            Collectors.counting()))
                    .forEach((key, value) -> System.out.println(key + " 订单数" + value));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 销量最高的月份
     */
    public static void case2() {
        try (Stream<String> lines = Files.lines(Path.of(fileUrl))) {
            Map.Entry<YearMonth, Long> map = lines.skip(1).map(e -> e.split(",")[TIME])
                    .collect(Collectors.groupingBy(s ->
                                    YearMonth.from(formatter.parse(s)),
                            Collectors.counting()))
                    .entrySet()
                    .stream()
                    .max(Map.Entry.comparingByValue()).orElse(null);
            System.out.println(map);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 销量最高的商品
     */
    public static void case3() {
        try (Stream<String> lines = Files.lines(Path.of(fileUrl))) {
            Map.Entry<String, Long> map = lines.skip(1).map(e -> e.split(",")[PRODUCT_ID])
                    .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
                    .entrySet()
                    .stream()
                    .max(Map.Entry.comparingByValue()).orElse(null);
            System.out.println(map);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 下单最多的前10用户
     */
    public static void case4() {
        try (Stream<String> lines = Files.lines(Path.of(fileUrl))) {
            lines.skip(1).map(e -> e.split(",")[USER_ID])
                    .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
                    .entrySet()
                    .stream()
//                    .sorted((o1, o2) -> o2.getValue().compareTo(o1.getValue()))
                    .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
                    .limit(10)
                    .toList()
                    .forEach(item -> System.out.println(item.getKey() + " 订单数" + item.getValue()));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 自定义小顶堆
     *
     * @param <E>
     */
    public static class MyQueue<E> extends PriorityQueue<E> {
        private final int size;

        public MyQueue(Comparator<? super E> comparator, int size) {
            super(comparator);
            this.size = size;
        }

        @Override
        public boolean offer(E e) {
            boolean r = super.offer(e);
            if (this.size() > size) {
                this.poll();
            }
            return r;
        }
    }

    /**
     * 下单最多的前10用户 小顶堆实现
     */
    public static void case5() {
        try (Stream<String> lines = Files.lines(Path.of(fileUrl))) {
            MyQueue<Map.Entry<String, Long>> queue = lines.skip(1).map(e -> e.split(",")[USER_ID])
                    .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
                    .entrySet()
                    .stream()
                    .collect(() ->
                                    new MyQueue<>(Map.Entry.comparingByValue(), 10),
                            MyQueue::offer,
                            AbstractQueue::addAll);
            while (!queue.isEmpty()) {
                Map.Entry<String, Long> entry = queue.poll();
                System.out.println(entry.getKey() + " 订单数" + entry.getValue());
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 每个地区下单最多的用户
     */
    public static void case6() {
        try (Stream<String> lines = Files.lines(Path.of(fileUrl))) {
            lines.skip(1).map(e -> e.split(","))
                    .collect(Collectors.groupingBy(e -> e[USER_REGION], Collectors.groupingBy(e -> e[USER_ID], Collectors.counting())))
                    .entrySet()
                    .stream()
                    .map(e -> Map.entry(e.getKey(), e.getValue().entrySet().stream().max(Map.Entry.comparingByValue())))
                    .forEach(System.out::println);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 每个地区下单前3的用户
     */
    public static void case7() {
        try (Stream<String> lines = Files.lines(Path.of(fileUrl))) {
            lines.skip(1).map(e -> e.split(","))
                    .collect(Collectors.groupingBy(e -> e[USER_REGION], Collectors.groupingBy(e -> e[USER_ID], Collectors.counting())))
                    .entrySet()
                    .stream()
                    .map(e -> Map.entry(e.getKey(),
//                            e.getValue().entrySet().stream().sorted(Map.Entry.<String, Long>comparingByValue().reversed()).limit(3).toList())
                                    // 堆排序
                                    e.getValue().entrySet().stream().collect(() -> new MyQueue<>(Map.Entry.<String, Long>comparingByValue(), 3), MyQueue::offer, AbstractQueue::addAll))
                    )
                    .forEach(System.out::println);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 按类别统计
     */
    public static void case8() {
        try (Stream<String> lines = Files.lines(Path.of(fileUrl))) {
            Map<String, Long> map = lines
                    .skip(1)
                    .map(e -> e.split(","))
                    .filter(e -> !e[CATEGORY_CODE].isEmpty())
                    .collect(Collectors.groupingBy(e -> e[CATEGORY_CODE], TreeMap::new, Collectors.counting()));
            map.forEach((key, value) -> System.out.println(key + " 订单数" + value));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 按一级类别统计销量
     */
    public static void case9() {
        try (Stream<String> lines = Files.lines(Path.of(fileUrl))) {
            Map<String, Long> map = lines
                    .skip(1)
                    .map(e -> e.split(","))
                    .filter(e -> !e[CATEGORY_CODE].isEmpty())
                    .collect(Collectors.groupingBy(e -> e[CATEGORY_CODE].split("\\.")[0], TreeMap::new, Collectors.counting()));
            map.forEach((key, value) -> System.out.println(key + " 订单数" + value));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 按价格区间统计销量
     */
    public static void case10() {
        try (Stream<String> lines = Files.lines(Path.of(fileUrl))) {
            Map<String, Long> map = lines
                    .skip(1)
                    .map(e -> e.split(","))
                    .map(e -> new BigDecimal(e[PRICE]))
                    .collect(Collectors.groupingBy(Statistics::priceRange, TreeMap::new, Collectors.counting()));
            map.forEach((key, value) -> System.out.println(key + " 订单数" + value));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static String priceRange(BigDecimal price) {
        if (price.compareTo(new BigDecimal(100)) < 0) {
            return "[0,100)";
        } else if (price.compareTo(new BigDecimal(500)) < 0) {
            return "[100,500)";
        } else if (price.compareTo(new BigDecimal(1000)) < 0) {
            return "[500,1000)";
        } else {
            return "[1000,∞)";
        }
    }

    /**
     * 不同年龄段女性所下不同类别订单
     */
    public static void case11() {
        try (Stream<String> lines = Files.lines(Path.of(fileUrl))) {
            TreeMap<String, TreeMap<String, Long>> map = lines.skip(1)
                    .map(e -> e.split(","))
                    .filter(e -> !e[CATEGORY_CODE].isEmpty() && e[USER_SEX].equals("女"))
                    .collect(Collectors.groupingBy(Statistics::ageRange,
                            TreeMap::new,
                            Collectors.groupingBy(e -> e[CATEGORY_CODE].split("\\.")[0], TreeMap::new, Collectors.counting())));
            map.forEach((key1, value1) -> value1.forEach((key, value) -> System.out.println(key1 + " " + key + " " + value)));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static String ageRange(String[] array) {
        int age = Double.valueOf(array[USER_AGE]).intValue();
        if (age < 18) {
            return "[0,18)";
        } else if (age < 30) {
            return "[18,30)";
        } else if (age < 50) {
            return "[30,50)";
        } else {
            return "[50,∞)";
        }
    }
}
